package okw.database;

import java.util.List;

import okw.database.model.MessagePackage;
import okw.database.model.MessageReceived;
import okw.database.model.MessageToSend;
import okw.database.model.Node;
import okw.database.repositories.MessagePackageRepository;
import okw.database.repositories.MessageReceivedRepository;
import okw.database.repositories.MessageToSendRepository;
import okw.database.repositories.NodeRepository;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import core.communication.message.Direction;
import core.communication.message.Message;
import core.communication.message.MessageType;
import core.utils.Converter;

@Service
@Transactional
public class DatabaseServiceImpl implements DatabaseService {

	private static final Logger L = LoggerFactory.getLogger(DatabaseServiceImpl.class);

	@Autowired
	private NodeRepository nodeRepository;
	@Autowired
	private MessageToSendRepository messageToSendRepository;
	@Autowired
	private MessageReceivedRepository messageReceivedRepository;
	@Autowired
	private MessagePackageRepository messagePackageRepository;

	public DatabaseServiceImpl() {
		L.debug("Tworzę DatabaseService");
		L.debug("Utworzono DatabaseService");
	}

	@Override
	public Node getMainNode() {
		return nodeRepository.findByLevel(0);
	}

	@Override
	public Node storeNode(Node node) {
		node = nodeRepository.saveAndFlush(node);

		L.debug("Dodałem nowy węzeł: {}", node);

		return node;
	}

	@Override
	public void deleteNode(Node node) {
		L.debug("Usuwam obiekt: {}", node);
		nodeRepository.delete(node);
	}

	@Override
	public List<Node> getNodesForDirection(Direction direction) {
		if (direction == Direction.UP)
			return nodeRepository.findAllByLevel(1);
		else
			return nodeRepository.findAllByLevel(-1);
	}

	@Override
	public List<MessageToSend> getMessagesToSend() {
		L.debug("Pobieram messagesToSend");

		return messageToSendRepository.findAll();
	}

	@Override
	public List<MessageReceived> getMessagesReceived() {
		L.debug("Pobieram messagesReceived");

		return messageReceivedRepository.findAllByIsHandledFalseOrderByCreatedAsc();
	}

	@Override
	public void saveReceivedMessage(Message message) {
		L.debug("saveReceivedMessage {}", message);

		MessageReceived messageR = new MessageReceived();
		messageR.setValue(Converter.objectToXML(message));
		messageR.setType(message.getMessageType());
		messageR = messageReceivedRepository.save(messageR);
		message.setMessageToSendDatabaseId(messageR.getId());
	}

	@Override
	public void setMessageAsHandled(Long messageToSendDatabaseId) {
		MessageReceived message = messageReceivedRepository.findOne(messageToSendDatabaseId);
		message.setIsHandled(true);
		L.debug("Zapisuje obsłużenie wiadomości {}", message);
		messageReceivedRepository.save(message);
	}

	@Override
	public Boolean isNodeLocatorUriTaken(String ip, String port) {
		return nodeRepository.findByIpAndPort(ip, port) != null;
	}

	@Override
	public void removeMessageToSend(Long messageToSendDatabaseId) {
		L.debug("Usuwam messagesToSend o id: {}", messageToSendDatabaseId);

		messageToSendRepository.delete(messageToSendDatabaseId);
	}

	@Override
	public void storeMessageToSend(Message message) {
		L.debug("saveMessageToSend {}", message);

		MessageToSend messageTS = new MessageToSend();
		messageTS.setValue(Converter.objectToXML(message));
		messageTS.setType(message.getMessageType());
		messageTS = messageToSendRepository.save(messageTS);
		message.setMessageToSendDatabaseId(messageTS.getId());
	}

	@Override
	public void deleteNode(String locatorUri) {
		Node node = nodeRepository.findByIpAndPort(Node.getIp(locatorUri), Node.getPort(locatorUri));
		nodeRepository.delete(node);
	}

	@Override
	public Node updateNode(String targetNodeUri, Node newNode) {
		Node node = nodeRepository.findByIpAndPort(Node.getIp(targetNodeUri), Node.getPort(targetNodeUri));
		node.setName(newNode.getName());
		node.setId(newNode.getId());
		node.setPort(newNode.getPort());
		node.setAddress(newNode.getAddress());
		node.setLevel(newNode.getLevel());
		node.setNodeType(newNode.getNodeType());

		return nodeRepository.saveAndFlush(node);
	}

	@Override
	public List<MessagePackage> getVotesPackages() {
		return messagePackageRepository.findAllByType(MessageType.VotesPackage);
	}

	@Override
	public List<MessagePackage> getVotersPackages() {
		return messagePackageRepository.findAllByType(MessageType.VotersPackage);
	}

	@Override
	public void deletePackages(List<MessagePackage> packages) {
		messagePackageRepository.delete(packages);
	}

	@Override
	public void storePackage(Message message) {
		messagePackageRepository.saveAndFlush(new MessagePackage(message));
	}
}
